﻿using System;
using System.ComponentModel;
using Finance.Annotations;
using Finance.Data.Entities;

namespace Finance.GUI.ViewModels
{
	/// <summary>
	/// ViewModel for DocumentProduct model.
	/// </summary>
	internal class DocumentProductViewModel : INotifyPropertyChanged	
	{
		private decimal _amount;
		private decimal _totalPrice;
		private string _productName;
		private decimal _price;
		private decimal _discountPercent;
		private IDocumentProduct _documentProduct;
		
		public IDocumentProduct DocumentProduct
		{
			get { return _documentProduct; }
			protected set
			{
				_documentProduct = value;
				if (_documentProduct == null)
				{
					_amount = 1;
					_productName = string.Empty;
					return;
				}
				_amount = _documentProduct.Amount;
				_price = _documentProduct.Price;
				_totalPrice = _documentProduct.TotalPrice;
				_productName = _documentProduct.ProductName;
				_discountPercent = _documentProduct.Discount*100;
			}
		}

		public DocumentProductViewModel(IDocumentProduct documentProduct)
		{
			DocumentProduct = documentProduct;
		}

		protected void SetAmount(decimal value)
		{
			if (_amount == value)
				return;

			_amount = value;
			OnPropertyChanged("Amount");
		}

		protected void SetPrice(decimal value)
		{
			if (_price == value)
				return;

			_price = value;
			OnPropertyChanged("Price");
		}

		public static decimal Round(decimal value, int digits)
		{
            var scale = (decimal)Math.Pow(10.0, digits);
			var round = decimal.Floor(Math.Abs(value) * scale + 0.5m);
			return (Math.Sign(value) * round / scale);
		}

		private void SetTotalPrice(decimal value)
		{
			var roundedValue = Round(value, 2);
			if (_totalPrice == roundedValue)
				return;

			_totalPrice = roundedValue;
			OnPropertyChanged("TotalPrice");
		}

		private bool SetDiscountPercent(decimal value)
		{
			if (_discountPercent == value)
				return false;

			_discountPercent = value;
			OnPropertyChanged("DiscountPercent");
			return true;
		}

		private bool _amountWasNotChanged = true;
		private bool _priceWasNotChanged = true;
		private bool _totalPriceWasNotChanged = true;

		protected void UpdateTotalPrice()
		{
			SetTotalPrice(_price * _amount * (1m - _discountPercent / 100m));
		}

		protected void UpdateAmount()
		{
			SetAmount(_totalPrice / (_price * (1m - _discountPercent / 100m)));
		}

		protected void UpdatePrice()
		{
			SetPrice(_totalPrice / (_amount * (1m - _discountPercent / 100m)));
		}
		
		public decimal Amount
		{
			get { return _amount; }
			set
			{
				SetAmount(value);

				_amountWasNotChanged = value == 0;

				if (_totalPriceWasNotChanged)
					UpdateTotalPrice();
				else if (_priceWasNotChanged)
					UpdatePrice();
			}
		}

		public decimal DiscountPercent
		{
			get { return _discountPercent; }
			set
			{
				if (!SetDiscountPercent(value))
					return;

				if(_totalPriceWasNotChanged)
					UpdateTotalPrice();
			}
		}

		public decimal Price
		{
			get { return _price; }
			set
			{
				SetPrice(value);

				_priceWasNotChanged = value == 0;

				if (_totalPriceWasNotChanged)
					UpdateTotalPrice();
				else if (_amountWasNotChanged)
					UpdateAmount();
			}
		}

		public decimal TotalPrice
		{
			get { return _totalPrice; }
			set
			{
				SetTotalPrice(value);

				_totalPriceWasNotChanged = value == 0;

				if (_priceWasNotChanged)
					UpdatePrice();
				else if (_amountWasNotChanged)
					UpdateAmount();
			}
		}

		
		public string ProductName
		{
			get { return _productName; }
			set { _productName = value; }
		}

		public static int MaxProductNameLength = 64;
		
		public void ApplyChanges()
		{
			if (_documentProduct == null)
				return;
			_documentProduct.TotalPrice = _totalPrice;
			_documentProduct.Price = _price;
			_documentProduct.ProductName = 
				_productName.Length <=MaxProductNameLength 
				? _productName 
				: _productName.Substring(0, MaxProductNameLength);
			_documentProduct.Amount = _amount;
			_documentProduct.Discount = _discountPercent / 100;
		}

		public void AttachDocumentProduct(IDocumentProduct documentProduct)
		{
			_documentProduct = documentProduct;
			ApplyChanges();
		}

		public event PropertyChangedEventHandler PropertyChanged;

		[NotifyPropertyChangedInvocator]
		protected virtual void OnPropertyChanged(string propertyName)
		{
			if (PropertyChanged != null) 
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
		}
	}
}